package com.jy.rhin.application.document;

import cn.org.chiss.rhin._2015.document.repository.ProvideAndRegisterDocumentSet;
import com.jy.framework.base.entity.Page;
import com.jy.rhin.application.assembler.CdaAssembler;
import com.jy.rhin.application.enums.DocumentTypeEnum;
import com.jy.rhin.config.ws.WsClientFactory;
import com.jy.rhin.facade.controller.dto.FileDto;
import com.jy.rhin.facade.dto.DocumentDetailDto;
import com.jy.rhin.facade.dto.DocumentManagerDTO;
import com.jy.rhin.facade.dto.TemplateRespDto;
import com.jy.rhin.facade.ws.document.storage.DocumentStorage;
import com.jy.rhin.infra.exception.RhinAggrErrorType;
import com.jy.rhin.infra.repo.document.content.query.DocumentContentQueryHandler;
import com.jy.rhin.infra.repo.document.submission.query.DocumentSubmissionQueryHandler;
import com.jy.rhin.infra.repo.document.template.mapper.DocumentTemplateMapper;
import com.jy.rhin.infra.repo.document.template.po.DocumentTemplatePo;
import com.jy.rhin.infra.repo.document.template.query.DocumentTemplateQueryHandler;
import com.jy.rhin.sdk.dto.document.DocumentContentDto;
import com.jy.rhin.sdk.dto.document.cda.ClinicalDocumentData;
import com.jy.rhin.sdk.dto.document.query.*;
import com.jy.rhin.sdk.dto.document.template.DocumentTemplateDto;
import com.jy.rhin.sdk.dto.document.template.QueryTemplateDto;
import com.jy.rhin.support.parsing.BaseDocumentParser;
import com.jy.rhin.support.util.Asserts;
import com.jy.rhin.support.util.DesensitizationUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.JXDocument;
import org.seimicrawler.xpath.JXNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Description:
 * @Author: lww
 * @Date: 2022/6/10 9:33
 */
@Slf4j
@Service
public class DocumentBizService {

    @Resource
    private DocumentContentQueryHandler documentContentQueryHandler;
    @Resource
    private DocumentSubmissionQueryHandler documentSubmissionQueryHandler;

    @Resource
    DocumentTemplateQueryHandler documentTemplateQueryHandler;

    @Resource
    DocumentTemplateMapper documentTemplateMapper;
    @Resource
    ApplicationContext context;
    
    @Autowired
    CdaAssembler cdaAssembler;

    private static final String CLINICAL_DOCUMENT = "$.ClinicalDocument";

    private static final String FILE_NAME = "共享文档";
    
    @Autowired
    private WsClientFactory factory;

    /**
     * 解析的文档类型Map集合
     */
    private EnumMap<DocumentTypeEnum, BaseDocumentParser> parsingMap = new EnumMap<>(DocumentTypeEnum.class);

    @PostConstruct
    public void init() {
        Set<Map.Entry<String, BaseDocumentParser>> entries = context.getBeansOfType(BaseDocumentParser.class).entrySet();
        entries.forEach(parsing -> parsingMap.put(parsing.getValue().getDocumentType(), parsing.getValue()));
    }

    /**
     * 分页查询文档列表
     *
     * @param dto
     * @return
     */
    public Page<DocumentManagerDTO> queryPage(DocumentManagerReqDto dto) {
        Page<DocumentManagerDTO> documentManagerPage = documentSubmissionQueryHandler.getDocumentManagerPageNew(dto);
        List<DocumentManagerDTO> records = documentManagerPage.getRecords();
        if (!CollectionUtils.isEmpty(records)) {
            records.forEach(documentManagerRespDto -> {
                documentManagerRespDto.setHuanZheXm(DesensitizationUtils.nameDesensitization(documentManagerRespDto.getHuanZheXm()));
                documentManagerRespDto.setZhengJianHm(DesensitizationUtils.cardDesensitization(documentManagerRespDto.getZhengJianHm()));
            });
            documentManagerPage.setRecords(records);
        }
        return documentManagerPage;
    }

    /**
     * 查询文档详情
     *
     * @param dto
     */
    public DocumentDetailDto queryDetail(GetContentDto dto) {
        DocumentDetailDto detailDto = new DocumentDetailDto();
        //查询文档内容
        DocumentContentDto contentResp = documentContentQueryHandler.getContent(dto);
        if (Objects.nonNull(contentResp)) {
            String content = contentResp.getContent();
            String covertContent = new String(Base64.getDecoder().decode(content.getBytes(StandardCharsets.UTF_8)));
            detailDto.setXml(covertContent);
            //根据文档类型获取文档模板字段
            QueryTemplateDto queryTemplateDto = new QueryTemplateDto();
            String typeCode = contentResp.getTypeCode();
            queryTemplateDto.setDocumentType(typeCode);
            List<DocumentTemplateDto> documentTemplateDtoList = documentTemplateQueryHandler.queryTemplate(queryTemplateDto.getDocumentType());
            List<TemplateRespDto> templateRespList = new ArrayList<>();
            if (!CollectionUtils.isEmpty(documentTemplateDtoList)) {
                templateRespList = documentTemplateDtoList.stream().map(templateDto -> {
                    TemplateRespDto templateRespDto = new TemplateRespDto();
                    templateRespDto.setYuanSuMC(templateDto.getElementName());
                    templateRespDto.setType(templateDto.getType());
                    templateRespDto.setShuoMing(templateDto.getInstructions());
                    templateRespDto.setValueDesc(templateDto.getValueDesc());
                    return templateRespDto;
                }).collect(Collectors.toList());
            }
            try {
                /*//xml转json
                JSONObject xmlJsonObj = XML.toJSONObject(covertContent);
                String jsonStr = xmlJsonObj.toString();
                Object json = JSONPath.read(jsonStr, CLINICAL_DOCUMENT);
                if (Objects.nonNull(json)) {
                    //获取文档类型枚举
                    DocumentTypeEnum documentType = DocumentTypeEnum.lookup(typeCode);
                    //获取调用的文档解析器
                    BaseDocumentParser parsing = getParsing(documentType);
                    //调用文档解析
                    List<TemplateRespDto> templateRespDtoList = parsing.documentParsing(json, templateRespList);
                    detailDto.setTemplateDtoList(templateRespDtoList);
                }*/
                DocumentTypeEnum documentType = DocumentTypeEnum.lookup(typeCode);
                //获取调用的文档解析器
                BaseDocumentParser parsing = getParsing(documentType);
                detailDto.setDocumentDetailV2Dto(parsing.documentParsingV2(covertContent,templateRespList));
            } catch (Exception e) {
                e.printStackTrace();
                log.error("xml解析失败");
            }
        }
        return detailDto;

    }


    /**
     * 获取对应的文档解析器
     *
     * @param documentType
     * @return
     */
    private BaseDocumentParser getParsing(DocumentTypeEnum documentType) {

        Asserts.nonNull(documentType, RhinAggrErrorType.DOCUMENT_TYPE_NULL_ERROR);
        BaseDocumentParser parsing = parsingMap.get(documentType);
        Asserts.nonNull(parsing, RhinAggrErrorType.DOCUMENT_TYPE_NOT_HANDLE);
        return parsing;
    }


    /**
     * 下载xml内容
     *
     * @param response
     * @param dto
     * @return
     */
    public void downloadXml(HttpServletResponse response, GetContentDto dto) {
        DocumentContentDto contentDto = documentContentQueryHandler.getContent(dto);
        if (Objects.nonNull(contentDto) && !StringUtils.isEmpty(contentDto.getContent())) {
            try {


                String content = contentDto.getContent();
                String xmlStr = new String(Base64.getDecoder().decode(content.getBytes(StandardCharsets.UTF_8)));
                response.addHeader("Accept-Language", "zh-cn");
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/octet-stream");
                String fileName = getFileName(contentDto.getHuanZheXm(), contentDto.getTitle());
                String fileNameEncode = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
                response.setHeader("Content-disposition", "attachment; filename=" + fileNameEncode + ".xml");
                PrintWriter writer = response.getWriter();
                writer.write(xmlStr);
                writer.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 批量下载xml
     *
     * @param response
     * @param dto
     */
    public void batchDownloadXml(HttpServletResponse response, GetContentReqDto dto) {
        List<GetContentRespDto> documentContentList = documentContentQueryHandler.getDocumentContentList(dto);
        List<FileDto> fileList = new ArrayList<>();
        List<String> fileNameList = new ArrayList<>();
        int index = 1;
        for (GetContentRespDto getContentResp : documentContentList) {
            String content = getContentResp.getContent();
            String xmlStr = new String(Base64.getDecoder().decode(content.getBytes(StandardCharsets.UTF_8)));
            String fileName = getFileName(getContentResp.getHuanZheXm(), getContentResp.getTitle());
            if (fileNameList.contains(fileName)) {
                fileName = fileName + index;
                index++;
            }
            fileNameList.add(fileName);
            FileDto file = new FileDto();
            file.setFileName(fileName);
            file.setOutByte(xmlStr.getBytes(StandardCharsets.UTF_8));
            fileList.add(file);
        }
        //文件压缩打包
        zipFiles(fileList, response);
    }

    /**
     * 获取文件名称
     *
     * @param huanZheXm
     * @param title
     * @return
     */
    private String getFileName(String huanZheXm, String title) {
        Map<String,String> names = new HashMap<>();
        names.put("个人基本健康信息登记","EHR-SD-01-个人基本信息-");
        names.put("出生医学证明","EHR-SD-02-出生医学证明-");
        names.put("新生儿家庭访视","EHR-SD-03-新生儿家庭访视-");
        names.put("儿童健康体检","EHR-SD-04-儿童健康体检-");
        names.put("首次产前随访服务","EHR-SD-05-首次产前随访服务-");
        names.put("产前随访服务","EHR-SD-06-产前随访服务-");
        names.put("产后访视","EHR-SD-07-产后访视-");
        names.put("产后42天健康检查","EHR-SD-08-产后42天健康检查-");
        names.put("预防接种报告","EHR-SD-09-预防接种报告-");
        names.put("传染病报告","EHR-SD-10-传染病报告-");
        names.put("死亡医学证明","EHR-SD-11-死亡医学证明-");
        names.put("高血压患者随访服务","EHR-SD-12-高血患者随记服务-");
        names.put("2型糖尿病患者随访服务","EHR-SD-13-2型糖尿病患者随访服务-");
        names.put("重性精神疾病患者个人信息登记","EHR-SD-14-重性精神疾病患者个人信息登记-");
        names.put("重性精神疾病患者随访服务","EHR-SD-15-重性精神疾病患者随访服务-");
        names.put("成人健康体检","EHR-SD-16-成人健康体检-");
        names.put("门诊摘要","EHR-SD-17-门诊摘要-");
        names.put("住院摘要","EHR-SD-18-住院摘要-");
        names.put("会诊记录","EHR-SD-19-会诊记录-");
        names.put("转诊（院）记录","EHR-SD-20-转诊（院）记录-");
        String fileName = null;
        if (!StringUtils.isEmpty(huanZheXm) && !StringUtils.isEmpty(title)) {
            fileName = names.get(title)+huanZheXm+ "-" +System.currentTimeMillis();
        } else if (!StringUtils.isEmpty(title)) {
            fileName = title;
        } else {
            fileName = FILE_NAME;
        }
        return fileName;
    }


    /**
     * 多个文件压缩成压缩包并下载
     *
     * @param fileList
     * @param httpResponse
     */
    private void zipFiles(List<FileDto> fileList, HttpServletResponse httpResponse) {
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(httpResponse.getOutputStream())) {
            httpResponse.addHeader("Accept-Language", "zh-cn");
            httpResponse.setCharacterEncoding("UTF-8");
            //下载压缩包
            httpResponse.setContentType("application/zip");
            httpResponse.setHeader("Content-disposition", "attachment;fileName=" + URLEncoder.encode(FILE_NAME + ".zip", StandardCharsets.UTF_8.toString()));
            for (FileDto fileDto : fileList) {
                String fileName = fileDto.getFileName();
                ZipEntry zipEntry = new ZipEntry(fileName);
                zipOutputStream.putNextEntry(zipEntry);
                zipOutputStream.write(fileDto.getOutByte());
                zipOutputStream.closeEntry();
            }
        } catch (IOException e) {
            e.printStackTrace();
            log.error("压缩文件失败 ", e);
        }
    }
    
    public void register(ClinicalDocumentData data) {
    	ProvideAndRegisterDocumentSet message = cdaAssembler.documentDataToCdaWSData(data);
    	DocumentStorage ws = factory.getClient(DocumentStorage.class);
    	ws.providerAndRegistryDocumentSet(message);
    }

    public void initTemplate(String xml,String type){
        JXDocument jxDocument = JXDocument.create(Parser.xmlParser().parseInput(xml, ""));
        String url = "//ClinicalDocument";
        Element root = jxDocument.selNOne(url).asElement();
        initTemplate(type,root.children(),url);
    }
    private boolean isMatch(String regex,String input){
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(input);
        return matcher.matches();
    }
    public String getCodeValue(JXNode j ){
        if(j!=null){
            Element code = j.asElement();
            String codeValue = code.attr("code");
            if(StringUtils.isEmpty(codeValue)){
                codeValue = code.attr("displayName");
            }
            String regex = "^DE\\d{2}\\.\\d{2}\\.\\d{3}\\.\\d{2}$";
            String regex3 = "^[\\u4e00-\\u9fa5]+$";
            if(  StringUtils.isNotEmpty(codeValue)&& (isMatch(regex,codeValue) ||
                    codeValue.contains("-") || isMatch(regex3,codeValue))  ){
                return "@code=" + codeValue;
            }
        }
        return "";
    }

    private void initTemplate(String type,Elements children,String url){
        if(!children.isEmpty()){
            for(Element c :children){

                String childUrl = url+"/"+c.nodeName();
                JXNode code = new JXNode(c).selOne("/code");

                String codeValue = getCodeValue(code);
                //值域code不做处理
                if (StringUtils.isNotEmpty(codeValue)) {
                    childUrl +=  codeValue;
                }
                if(!"//ClinicalDocument/component/structuredBody".equals(childUrl)
                    && !"//ClinicalDocument/component".equals(childUrl)){
                    insertTemplate(type,childUrl);
                }
                initTemplate(type,c.children(),childUrl);
            }
        }
    }

    private void insertTemplate(String type,String url){
        DocumentTemplatePo po = new DocumentTemplatePo();
        po.setDocumentType(type);
        po.setElementName(url);
        po.setInstructions("");
        if(url.startsWith("//ClinicalDocument/component/structuredBody/component")){
            po.setType("2");
        }else{
            po.setType("1");
        }
        documentTemplateMapper.insert(po);
    }
    

}
